Skip to content

Decode BOLT12 invoices in the DecodeInvoice API#215

Open
vincenzopalazzo wants to merge 2 commits into
lightningdevkit:mainfrom
vincenzopalazzo:claude/stupefied-mendeleev-50a203
Open

Decode BOLT12 invoices in the DecodeInvoice API#215
vincenzopalazzo wants to merge 2 commits into
lightningdevkit:mainfrom
vincenzopalazzo:claude/stupefied-mendeleev-50a203

Conversation

@vincenzopalazzo
Copy link
Copy Markdown
Contributor

Summary

  • DecodeInvoice now also decodes a hex-encoded BOLT12 invoice, not just a BOLT11 invoice string.
  • DecodeInvoiceResponse gains a kind field ("bolt11" or "bolt12") identifying which invoice type was decoded.
  • A BOLT12 invoice has no human-readable string form (it is exchanged as raw bytes over onion messages), so the input is expected to be hex-encoded and is parsed via LDK's Bolt12Invoice::try_from. Per the intentionally minimal scope, only kind is populated for a BOLT12 invoice; the remaining fields continue to apply to BOLT11 invoices.
  • CLI / MCP / client docs updated to reflect the broadened input.

Test plan

  • cargo test — new unit tests in decode_invoice.rs cover the BOLT12 invoice round-trip and rejection of unparseable / non-invoice input
  • e2e test_cli_decode_invoice and test_mcp_live_tool_calls assert kind == "bolt11"
  • cargo check --all-features --all-targets, cargo fmt --all, clippy clean for the changed code
  • Protobuf regenerated via RUSTFLAGS="--cfg genproto" cargo build -p ldk-server-grpc

AI tools: implemented with Claude Code (Claude Opus 4.7).

@ldk-reviews-bot
Copy link
Copy Markdown

ldk-reviews-bot commented May 20, 2026

I've assigned @benthecarman as a reviewer!
I'll wait for their review and will help manage the review process.
Once they submit their review, I'll check if a second reviewer would be helpful.

@benthecarman
Copy link
Copy Markdown
Collaborator

Do we even have a way to get a bolt 12 invoice?

@vincenzopalazzo
Copy link
Copy Markdown
Contributor Author

Do we even have a way to get a bolt 12 invoice?

Ah good point, and yes and no! With the new version of ldk-node we can perform a proof of payment (first version of it) with lightningdevkit/ldk-node#733 but people may want to use ldk to decode an bolt12 invoice that they are getting from somewhere else

Comment thread ldk-server/src/api/decode_invoice.rs Outdated
The `DecodeInvoice` RPC previously accepted only a BOLT11 invoice
string. It now also accepts a hex-encoded BOLT12 invoice, and the
response carries a new `kind` field ("bolt11" or "bolt12") identifying
which was decoded.

Unlike offers and BOLT11 invoices, a BOLT12 invoice has no
human-readable string encoding -- it is exchanged as raw bytes over
onion messages -- so the input is expected to be hex-encoded, and LDK's
`Bolt12Invoice` is parsed via `TryFrom<Vec<u8>>` accordingly.

For a decoded BOLT12 invoice the fields that map onto
`DecodeInvoiceResponse` are populated: `destination` (signing pubkey),
`payment_hash`, `amount_msat`, `timestamp` (`created_at`), `expiry`
(`relative_expiry`), `description`, `fallback_address`, `features`, and
`is_expired`. BOLT11-only fields (`payment_secret`, `route_hints`,
`currency`, ...) are left empty.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@vincenzopalazzo vincenzopalazzo force-pushed the claude/stupefied-mendeleev-50a203 branch from 86d444d to bcdb005 Compare May 21, 2026 09:07
Comment thread ldk-server/src/api/decode_invoice.rs
`DecodeInvoiceResponse` previously dropped the blinded payment paths of
a decoded BOLT12 invoice. It now carries them in a new `paths` field,
mirroring the `paths` already exposed for decoded BOLT12 offers. BOLT11
invoices leave the field empty, as they carry route hints instead.

The blinded-path-to-proto conversion is extracted from `decode_offer`
into a shared `blinded_path_to_proto` helper, so offer decoding (blinded
message paths) and invoice decoding (blinded payment paths) build the
same `BlindedPath` representation.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
pub is_expired: bool,
/// The kind of decoded invoice: "bolt11" or "bolt12".
#[prost(string, tag = "16")]
pub kind: ::prost::alloc::string::String,
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we make this a proper enum

Comment thread e2e-tests/tests/e2e.rs
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you add a e2e test

Comment thread e2e-tests/tests/mcp.rs
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you add a mcp test

Copy link
Copy Markdown
Collaborator

@tnull tnull left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, really not sure if we should make this part of the official API given that AFAIK BOLT12 invoices still don't have a specified encoding format? Yes, hex bytes work for the LDK type, but are we positive this works with other implementations?

I know there has been a lot of previous discussions around that, but should we maybe revisit whether we can add a blessed bech32 encoding for BOLT12 invoices before we make it part of our official API? It seems there is a requirement by now, and maybe we should account for that in the spec?

(cc @jkczyz @TheBlueMatt)

@vincenzopalazzo
Copy link
Copy Markdown
Contributor Author

Hmm, really not sure if we should make this part of the official API given that AFAIK BOLT12 invoices still don't have a specified encoding format? Yes, hex bytes work for the LDK type, but are we positive this works with other implementations?

Oh, right, I kind of forgot about it that LDK is not going to expose the Bolt12 Invoice, and this is linked to the reason that we do not have a real bech encoding!

Sorry, it completly when off my mind! Probably we should just wait for lightningdevkit/rust-lightning#4297

@tnull
Copy link
Copy Markdown
Collaborator

tnull commented May 22, 2026

Oh, right, I kind of forgot about it that LDK is not going to expose the Bolt12 Invoice, and this is linked to the reason that we do not have a real bech encoding!

Yeah, see the discussion over at lightningdevkit/rust-lightning#3800

I think we might want to reconsider that if we find that it's necessary for P-o-P (but I think it just adds lnp, right?), but it should probably discussed/reconsidered at the spec level first before we add an API surface for it.

@vincenzopalazzo
Copy link
Copy Markdown
Contributor Author

vincenzopalazzo commented May 22, 2026

The real problem I see here, I think, is not PoP but the migration from a CLN to the ldk-server. Currently, people who are using bolt12 are performing (the few who know what it is) proof of payer or proof that the invoice that they are paying is attached to the offer.

So, I see that there will be the need for this command, but I also see that with the payer proof update to the bolt12, this problem will be gone anyway, without exposing the bolt12 invoice.

However, it will require some time to do this migration

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants